perm filename IOSAIL.BTH[UP,DOC]1 blob sn#352208 filedate 1978-04-29 generic text, type C, neo UTF8
COMMENT ⊗   VALID 00018 PAGES
C REC  PAGE   DESCRIPTION
C00001 00001
C00003 00002	Changes to IOSAIL.
C00008 00003	Introduction.
C00010 00004	SPACE,TAB,BELL,FORMFD,LF,CR,CRLF,!,PROC,REF,THRU,UPTO,BEGINLOOP.
C00014 00005	NULLSTRING,CVBS,ALPHA,NUMER,MEMLOC,LOWBOUND,UPBOUND,NUMDIM.
C00017 00006	DEBUG,BUGON,BUGOFF.
C00023 00007	YES,NO,NUMERIC,LOWERCASE,UPPERCASE,ALPHABETIC,ALPHAMERIC.
C00025 00008	CAPITALIZE.
C00026 00009	ROUND.
C00027 00010	A discussion of channels.
C00030 00011	OPENREAD,OPENWRITE,CLOSEFILE,CLOSEALL.
C00033 00012	Filenames.
C00034 00013	The reads: LINE,INTEGER,REAL,STRING,WORD,YESNO,BUFFER,READ,RDTTY.
C00040 00014	EOF.
C00042 00015	GETPROMPT,SETPROMPT.
C00044 00016	SETREADER,GETREADER.
C00046 00017	SETECHO.
C00047 00018	TERM!PRINT!(ON | OFF),FILE!PRINT!(ON | OFF),FILE!PRINT!CLOSE.
C00049 ENDMK
C⊗;
Changes to IOSAIL.
 
∂4/28/78 -- LOADER BUG FIXED
The names FILE!PRINT!ON,FILE!PRINT!OFF,FILE!PRINT!CLOSE are too long
to be distinguished by the loader (since they were moved to IOSAIL.SAI)
therefore the procedures were renamed to be !!FPON,!!FPOFF,!!FPC and
the long names became macros in .HDR calling these new names for
the procedures.  The change should be transparent to the user except
that the new bang-bang (!!) names of course become "reserved words".

∂4/27/78 -- FILE!PRINT!ON,FILE!PRINT!OFF,FILE!PRINT!CLOSE,CLOSEALL
have been moved from macros in .hdr to procedures in .sai to
permit PROFILE to make more reasonable output for these features.
In addition CLOSEALL now closes a SETPRINT file that may be open.

∂4/25/78 -- IOSAIL.DOC[107,bth] ==> IOSAIL.BTH[up,doc]
	    IOSAIL.HDR[107,bth] ==> IOSAIL.HDR[sub,sys]
	    IOSAIL.REL[107,bth] ==> IOSAIL.REL[sub,sys]
	    IOSAIL.SAI[107,bth] ==> IOSAIL.SAI[sub,sys]

∂2/23/78 -- openread and openwrite bug fixed
prompting for filenames in openread and openwrite will now
work correctly even with setprint in use (due to changing a
print to an outstr).

∂2/21/78 -- new macros
newr=new!record
nullr=null!record

∂2/21/78 -- ROUND
new ROUND function see iosail.doc

∂2/21/78 -- READINTEGER "fixed"
READINTEGER has been changed to use cvd rather that intscan since
it was found that intscan uses realscan and can lose some integers.

∂2/13/78 -- tab bug fixed
When the iosail package was brought from lots tabs were changed to spaces,
hence tabs were not passed over correctly in the input routine (eg. read
integer).  This has been corrected.

∂2/7/78 -- new macros
R!C = record!class
R!P = record!pointer

∂1/30/78 -- openread / openwrite fixed
if on opening a filename, that file does not exist then iosail
now will permit the user to change device as well as file name.

∂1/26/78 -- availability of rdtty
RDTTY is now available for use by the user of IOSAIL.

∂1/26/78 -- rdtty correction
RDTTY now prompts with OUTSTR to the terminal  which ignores
the status of setprint.

∂1/26/78 -- prompt changes
Instead of prompting with ?>integer> IOSAIL now prompts with ?>(integer)
and similarly for other types.

∂1/26/78 -- new READ
READ is no longer a synonym for READREAL.  If the prompting is still the
default then READ will prompt with >? instead of ?>.  This is for
compatibility with Floyd's notes.

∂1/26/78 -- READBUFFER
a new string procedure that returns anything left in the input buffer.

∂1/23/78 -- new macros
SPACE = " ".
NEWPAGE = FORMFEED.
 
∂1/23/78 -- rdtty
Rdtty has been incorporated into IOSAIL and setty has been eliminated.
Introduction.



This document describes the  SAIL procedures and  macros written by  Kevin
Karplus <k.kjk> December 1977.  The package is intended for the CS105  and
CS106 classes, but is  available to all users  of the SAIL language.   The
package is being maintained  at SU-AI by Brent  Hailpern (BTH).  All  bugs
and suggetions should be sent to him.

To get all the macros and  procedures declared, put the statement  

REQUIRE "IOSAIL.HDR[SUB,SYS]" SOURCE!FILE;

in the declarations section of the  outermost block of your SAIL  program.
This will automatically include IOSAIL.REL[SUB,SYS]  in the list of  files
for  the   LOADER.   The   SAIL  sources   for  the   procedures  are   in
IOSAIL.SAI[SUB,SYS],    the    macros    and    declarations    are     in
IOSAIL.HDR[SUB,SYS].

There are several distinct parts  to this package:  macros whose  function
is that of abbreviation, some macros for debugging, some simple procedures
for frequently used character tests,  the input/output routines, and  some
macros for use with SETPRINT.
SPACE,TAB,BELL,FORMFD,LF,CR,CRLF,!,PROC,REF,THRU,UPTO,BEGINLOOP.
	(MARCOS defined in IOSAIL.HDR[SUB,SYS])

     The following are simple abbreviations that have proven useful in
the past.
TAB            ==> a string containing the tab character.
SPACE          ==> a string containing the space character.
BELL           ==> a string containing the bell character.
FORMFEED       ==> a string containing the formfeed character ↑L.
NEWPAGE = FORMFD = FORMFEED
LF = LINEFEED  ==> a string containing the linefeed character.
CR             ==> a string containing a carriage return.
CRLF = NEWLINE ==> the equivalent of CR & LF.
PLOTLF         ==> a string containing ↑E & LINEFEED. [LOTS]
!              ==> COMMENT
PROC           ==> PROCEDURE
REF            ==> REFERENCE
THRU           ==> step 1 until
UPTO           ==> :=1 step 1 until
BEGINLOOP      ==> while true do begin
R!C            ==> record!class
R!P            ==> record!pointer
NEWR	       ==> new!record
NULLR	       ==> null!record


     Note that the BEGINLOOP macro along with the DONE  construct  can
be  used  as  a  looping construct of great power and simplicity.  For
example:
     beginloop "example loop"
       temp:=READLINE;
       if EOF then DONE "example loop";
       !   do here whatever you want with temp;
                .
                .
                .
     end "example loop";
this program fragment reads lines of the main input  device  until  it
reaches  an end of file.  All the lines read are processed by the code
after the IF statement, and the the loop is terminated by satisfaction
of  the  end  of  file  test.   (The  procedures  READLINE and EOF are
described  later.)  The  capitalization  of  the  DONE  statement   is
encouraged,  to  make  it  stand out from the predominantly lower case
program body.

     Please remember that the  macro  for  comment  (!)  must  have  a
delimiter after it to be recognized as a character.  Thus "!ABCD" is a
legal variable, but "!  ABCD" is a comment.

The usual  uses  of FORMFEED,  TAB,  BELL, and  CRLF  are with  the  PRINT
statement to print them  on your terminal (or  with similar statements  to
add them to files).   You will find  CRLF the most useful  of this set  of
characters.  FORMFEED at the begining of a line tells the line printer  to
go to a new page before printing the line.  PLOTLF is not of much use  for
novices.  It  is  for  printing a  line  in  the "plotting"  mode  on  the
PRINTRONIX at LOTS.

NULLSTRING,CVBS,ALPHA,NUMER,MEMLOC,LOWBOUND,UPBOUND,NUMDIM.
	(MACROS defined in IOSAIL.HDR[SUB,SYS])


     Some other macros of interest are
NULLSTRING(x)  returns TRUE if the string x is  the  null  string  and
                   FALSE otherwise.
CVBS(x)        converts boolean to string "TRUE" or "FALSE"
ALPHA          ==> a string containing the  entire  alphabet  in  both
                   upper and lower cases.
NUMER          ==> a string containing all the digits.
MEMLOC(x)      ==> memory[location(x)].  (useful only to hackers)
LOWBOUND(arr,dim) returns the lower bound of the  DIMth  dimension  of
                   the array ARR at runtime.
UPBOUND(arr,dim) returns the upper bound of the DIMth dimension of the
                   array ARR at runtime.
NUMDIM(arr)    returns the  number  of  dimensions  of  array  ARR  at
                   runtime.


     ALPHA and NUMER have many uses, one of the most  important  being
to make it easier to set up "breaktables" for the SAIL SCAN statement.
Novices should not have to worry about this powerful  but  complicated
statement,  as  the  routines described later should be sufficient for
their needs.

LOWBOUND, UPBOUND, and NUMDIM are useful  if you use arrays with  variable
dimensions, but  need  to know  the  dimensions  at runtime  to  set  loop
parameters.  These macros  are particularly useful  inside of  procedures,
which can then be handed  arrays of any size and  still be able to  figure
out what to do with  them.  Note that NUMDIM  will be negative for  string
arrays (because of the expansion to the ARRINFO command).
DEBUG,BUGON,BUGOFF.

     This is a very powerful macro originally designed by J.Q.Johnson,
later slightly modified by Kevin Karplus.  It allows the user to print
out  selected  variables  at  any  point  in  a  program,  along  with
identification  of the variables and the location in the program.  The
output looks like:

DEBUG:after fiddling the foofram i=17.3; j=99.99; stq="jjjgh"jjitm";

which could have been created by a program segment like:
      i:=17.3;
      j:=99.99;
      stq:="jjjgh""jjitm";
      DEBUG(after fiddling the foofram,(i,j,stq));
Notice that the quote mark inside the string is not doubled by  DEBUG,
and  that the location identification is any string of characters that
satisfies the normal rules about arguments to macros.  For simplicity,
it is best to keep this to a few short simple words, without commas or
other punctuation.  

     Like most macros, DEBUG gets confused by commas  that  it  thinks
are  superfluous  or  misplaced.  Thus a statement like:  DEBUG(before
input,(innersecrets[1,2,j]));  is liable to give you an error  message
(or a mysterious DRYROT).  SAIL doesn't know about DEBUG, so the error
message will be exceedingly unhelpful.  There is,  of  course,  a  way
around.   (SAIL  abounds  in ways to get around things, it adds to its
mystery.) The particular hack involved is to put curly  braces  around
the  variable  that  is  causing  the  difficulty.  Thus the statement
DEBUG(before input,({innersecrets[1,2,j]},j));  would work.

     It is also possible to use expressions instead  of  variables  as
the  things  to be printed.  They will be printed as if they were in a
PRINT statement ,  and  will  be  labeled  with  the  expression  that
produced   them.    Use  of  expressions  is  not  guaranteed  by  the
implementors, so don't get upset if it doesn't work.

     As a further convenience, if you have only a single  variable  to
print,  you  can  omit  the  parentheses  around it.  "DEBUG(here,a);"
should be exactly the same as "DEBUG(here,(a));" .

     Sometimes great quantities of debug output can be generated, even
when  it  is  known  that the bug you are looking for only occurs when
certain special conditions occur.  While the computer cannot check the
phase of the moon or the absence of the TA, it is often easy to put in
checks to see whether the program has gotten a value that  is  strange
at  some  point,  and then turn on the DEBUG statements.  To make this
easier, two macros have been included:  BUGON  and  BUGOFF.   Normally
the  DEBUG  statements  are  "on"  and  print  output on the terminal.
Execution of BUGOFF turns off the  DEBUG  statements  until  the  next
execution  of  a  BUGON.   Note  that  the variable !bugoff is what is
changed by these macros, you shouldn't  use  that  variable  name  for
anything  else.  As an example, assume that the routine BUGGY is known
to die when it has to handle long strings.  Then something  like  this
fragment might help:
    BUGOFF;
       .
       .
       .
    if length(arg) > 56 then BUGON else BUGOFF;
    BUGGY(arg);
       .
       .
       .

     There is also a more general and more powerful debugging facility
available,  called  BAIL.  For most short student programs, the effort
involved in learning and using BAIL is  not  justified  by  the  small
extra debugging power it gives you.
YES,NO,NUMERIC,LOWERCASE,UPPERCASE,ALPHABETIC,ALPHAMERIC.
Some small simple procedures for dealing with strings

This section contains a number of very short, very simple procedures  that
are frequently  useful.   All are  boolean  procedures (ie.   they  return
either TRUE or FALSE).

The argument  to all  the procedures  is a  character.  This  can be  done
either by having the character be  the first character of a string  passed
to the  procedure, or  by passing  an  integer whose  value is  the  ASCII
representation of the character.  Passing  a string for interpretation  of
its first character is probably more common for most of these procedures.


YES(char) returns TRUE if the character is "Y" or "y".

NO(char) returns TRUE if the character is "N" or "n".

NUMERIC(char) returns TRUE if the character is a decimal digit.

LOWERCASE(char) returns TRUE if the character is a lower case letter.

UPPERCASE(char) returns TRUE if the character is an upper case letter.

ALPHABETIC(char) returns TRUE if the character is a letter.

ALPHAMERIC(char) returns TRUE if the character is either a letter or a
          digit.
CAPITALIZE.

CAPITALIZE(string) returns its string argument with all the lower case
          letters  converted  to  upper case (or, to be old fashioned,
          all  the  miniscules   converted   to   magiscules).    Thus
          CAPITALIZE("abcDEFmmm123 $ $")  returns  "ABCDEFMMM123 $ $".
          Note that the argument is left unchanged.
ROUND.
 
The round procedure does floor(x+.5) despite the compile switch setting
for real to integer coersion (it uses the FIXR FAIL command).
 
To be used:
i:=round(x)
where i is integer and x is real.
A discussion of channels.

Whenever input and output are mentioned in respect to  SAIL,  channels
are  almost  certain  to be mentioned.  A channel (in this context) is
simply a small number that gets associated with the file or device you
are  communicating  with,  so it isn't necessary to refer to the whole
name of the file and look for it each time you want to access it.  The
numbers  are  rather  arbitrarily  assigned,  and  have  no  intrinsic
meaning.
SAIL can handle  about 16  channels (0  through 15)  plus the  controlling
terminal (also  known as  TTY:) as  channel -1.   Channel -2  is also  the
terminal.  The channels used by this package are the same as SAIL channels
except:

     1.  When inputing through the procedures of this package, channel
         -1  means  the  "main reader channel".  This is the terminal,
         unless SETREADER is called to change it.

     2.  When inputting though the procedures of this package, channel
         -2 means the controlling terminal.

     3.  All the SAIL functions behave as normal, do NOT try printing on 
	 -1 nor should you try printing on channel -2.  To output to the
	 terminal use PRINT.

     4.  The normal mechanisms for opening and  closing  files  should
         not  be  employed  if the files are to be used with the other
         routines of this package.
OPENREAD,OPENWRITE,CLOSEFILE,CLOSEALL.

The process of finding the file you want  to  use  and  associating  a
channel  number  with  it  is  called  "opening  a file".  The similar
process of cleaning out the buffers and putting the file away when you
are done is called "closing a file".  

There are two routines in IOSAIL for opening a file,  called  OPENREAD
and OPENWRITE.  Both take the file name as an argument and both return
the channel associated with the file.  If no filename is given, (or if
the  null  string  is  given) then the OPEN...  procedures ask for the
file name from  the  teletype.   Note  that  TTY:   is  an  acceptable
filename  for  both  input  and  output (it means the terminal you are
running the program from).

The format of the commands looks like:
chan:=OPENREAD;
chan:=OPENREAD("FOO.in");
chan:=OPENWRITE;
chan:=OPENWRITE("FOO.OUT");

Please resist the temptation to use the .SAI extension for your output
files.  It is very easy to destroy programs that way.

The counterpart of "opening", "closing", is handled  just  as  easily.
CLOSEFILE(chan)  will  close  the file and free the channel number for
reassignment.  CLOSEFILE(-1) will close the reader channel and  return
channel -1 to its original status as the controlling input terminal.

There is a procedure CLOSEALL, which will close all the channels that  are
open (including any  setprint file), and  generally try to  set the  input
output system  as much  like its  initial state  as it  can.  To  do  this
massive clean-up, make:
CLOSEALL;
the last executable statement of your program.
Filenames.

It is possible to determine the filename of a channel  that  has  been
opened  by use of the string procedure FILENAME(chan).  If the channel
is connected to a terminal the string "TTY:" is returned.
The reads: LINE,INTEGER,REAL,STRING,WORD,YESNO,BUFFER,READ,RDTTY.
Procedures for input

There are several procedures for inputting various values from a  channel.
All of them have  the same basic calling  sequence, and behave in  similar
manners (except for RDTTY which is described below).  They are:  READLINE,
READINTEGER, READREAL, READSTRING, READWORD, READYESNO, READ,  READBUFFER.
The calling sequence is
foo:=READ...(chan);
foo:=READ...;
where READ...  means any of the READ routines mentioned above, and foo
is  of  the  appropriate  type for the particular routine.  If chan is
given, then the channel must be open for  reading.   If  chan  is  not
given, -1 is assumed and reading is from the reader channel.

READLINE is the most basic of these routines, and is called by all the
others.   It  reads a line (ignoring carriage return) until it finds a
linefeed, or end-of-file is reached.  If there are no more lines to be
read  from  the  file, a null string is returned, otherwise the string
read (without crlf) is returned.

READINTEGER and READREAL ignore blank, tabs, and crlfs until they get
to  a non-blank character.  If the character is not acceptable for the
first character of a number, an error message is  generated.   If  the
channel  is  connected  to a terminal, then the user is prompted for a
retry.  Otherwise the error is fatal, and the program aborts.

READ is the same as READREAL except  that if the prompting on the  channel
is the default  then the  prompting for  that particular  read becomes  >?
instead of ?>.

READSTRING  reads  a  string  beginning  and  ending  with  the  quote
character  (").  A quote can be included in the string by doubling it.
Thus the  string  "abc""DEF""ghi" would  be  read  as  abc"DEF"ghi  .
Blanks,  tabs,  and  crlfs  before  the opening " are ignored.  If the
first character after the blanks is not a quote, an error  message  is
printed.  (Again, fatal if not TTY, retry if TTY).

READWORD reads the next  punctuation  mark  or  string  of  alphameric
characters.  Leading blanks, tabs, and crlfs are ignored.  

READYESNO expects the next character (after blanks, tabs,  and  crlfs)
to  be  "Y","y",  "N",  or "n".  It then returns TRUE if it is a y and
FALSE if it is an n.  Any other character results in an error.  (Fatal
if not a TTY, retry if a tty).

READBUFFER returns the unread portion of the input buffer as a string.

RDTTY is called by RDTTY("prompt"), where ">" is default.  It prints  that
prompt on the terminal (ignoring SETPRINTs) and then reads a line from the
terminal and returns it as  a string.  RDTTY is  faster in that it  avoids
all overhead  of  READLINE(-2) and  permits  direct specification  of  the
prompt.  Its  drawback is  that  it bypasses  the  eof mechanism  for  the
terminal and should therefore be used with care.

Note:  READLINE always reads a fresh line from the terminal  or  file,
all  the other routines will keep reading on the same line until there
is nothing left of the line, or  a  READLINE  is  done.   Thus  it  is
possible  to read every non-blank of a file with repeated READWORD, or
every number with READREAL.  Line numbers in files are ignored.
EOF.

End-of-file handling

When one of the READ...  routines is called, and  can't  find  anymore
input, it returns an appropriate null value.  (Null strings for string
valued  procedures,  FALSE  for  READYESNO,   and   intscan(null)   or
realscan(null)  for READINTEGER and READREAL).  The EOF indication can
be tested with the procedure EOF(chan).  As with all  the  procedures,
if  the  channel  number  is  omitted, channel -1 s assumed.  EOF will
return TRUE, after the call which returned the null value.

Thus the correct test for end-of-file is:
    BEGINLOOP "readloop"
    temp:=READ...;
    if EOF then DONE "readloop";
    !  body of loop;
          .
          .
          .
    END "readloop"

Testing for end-of-file should be  done after every read.  An  end-of-file
for display can be accomplished by typing <control><meta><lf> (or  <ctrl>z
from a TTY).  However, only one  null return is made, subsequent calls  to
read from a channel associated with  a terminal will behave normally,  and
EOF will only  return TRUE  if the  last read on  the channel  hit the EOF
character.


GETPROMPT,SETPROMPT.

Prompting 

When reading from the terminal, the user is prompted for  input.   The
default  prompt for channels -2 and -1 is "?>", for the other channels
it is the channel number followed by ">".  The prompt  string  can  be
determined     by     foo:=GETPROMPT(chan),     and     changed     by
SETPROMPT(string,chan).

If the channel is not specified, then channel -1 is assumed.   If  the
string  to  SETPROMPT  is null then the prompt will be set back to the
default.

If the promptstring for a channel is  the  default  string,  then  the
routines  READINTEGER, READREAL, READSTRING, READWORD, READYESNO add a
further prompt to indicate what type of input is being requested.
SETREADER,GETREADER.

The use of channel -1 to access  the  main  reader  channel  has  been
mentioned  before.   To  associate  -1  with some real channel is very
easy.  First, OPENREAD should be called to create  the  channel,  then
SETREADER(chan)  will  make  chan the main reader channel.  The reader
channel can be reset to the terminal  (the  initial  state)  by  doing
SETREADER(-1).    SETREADER  without  an  argument  is  equivalent  to
SETREADER(-1).

GETREADER is an integer  valued  procedure  with  no  arguments  which
returns  the  channel  number  of  the  main  reader  channel.   If no
SETREADER call has been made, then GETREADER returns -1.


SETECHO.

Echoing

All the input through the main reader channel can be echoed to a file,
or  to  the  terminal.   This is accomplished by doing an OPENWRITE to
open a channel, and SETECHO(chan) to send the echoing to the  channel.
SETECHO(-1)  or SETECHO without arguments, turns off the echoing.  The
echo can be forced to the terminal by SETECHO(-2).

Note that you cannot echo to the file used in  a  SETPRINT  statement,
because  of the way SAIL handles SETPRINT.  However, it is possible to
do CPRINT to the same channel as the echoing.

TERM!PRINT!(ON | OFF),FILE!PRINT!(ON | OFF),FILE!PRINT!CLOSE.

Setprint control macors and procedures

There are two  macros and  three procedures in  this class,  and they  all
invoke SETPRINT with various arguments.   They serve to direct your  PRINT
output to your terminal, a file, or both.  Normally your PRINT output goes
just to your terminal, so you have to do something special if you want the
output to go to a file as well.  You could open a channel and write to  it
(using OPENWRITE and CPRINT), but this necessitates duplicating the  PRINT
statements and is often not worth the trouble.

First the  file should  be  established with  the  SETPRINT(filename,"B");
statement as  described int  the SAIL  manual.  Thereafter  TERM!PRINT!ON,
TERM!PRINT!OFF,  FILE!PRINT!ON,  FILE!PRINT!OFF,  FILE!PRINT!CLOSE  should
suffice for handling the various SETPRINT options.

Note:  the  FILE!PRINT!... identifiers  are  actually macros  calling  the
three procedures !!FPON,!!FPOFF,!!FPC so avoid using these bang-bang  (!!)
identifiers as variables in your programs.